package com.thoughtworks.yuyijq.generate;

import com.intellij.codeInsight.generation.PsiElementClassMember;
import com.intellij.codeInsight.generation.PsiFieldMember;
import com.intellij.ide.util.MemberChooser;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.thoughtworks.yuyijq.generate.element.ImportElement;
import com.thoughtworks.yuyijq.generate.element.MethodElement;
import com.thoughtworks.yuyijq.generate.template.Template;
import com.thoughtworks.yuyijq.generate.template.TemplateLoader;
import com.thoughtworks.yuyijq.generate.ui.MyChooserBuilder;
import org.apache.log4j.Logger;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

import javax.swing.*;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * User: yuyijq
 * Date: Oct 9, 2010
 * Time: 12:30:36 AM
 */
public class GenerateEqualsAndHashCodeHandler extends EditorWriteActionHandler {
    private static final Logger log = Logger.getLogger(GenerateEqualsAndHashCodeHandler.class);

    private String fixLineBreaks(String s) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (Character.LINE_SEPARATOR != c) {
                sb.append(c);
            }
        }

        return sb.toString();
    }

    @Override
    public void executeWriteAction(final Editor editor, DataContext dataContext) {
        Project project = editor.getProject();
        final PsiManager manager = PsiManager.getInstance(project);
        Editor textEditor = FileEditorManager.getInstance(project).getSelectedTextEditor();
        VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(textEditor.getDocument());
        PsiFile file = manager.findFile(virtualFile);
        if (file instanceof PsiJavaFile) {
            final PsiJavaFile javaFile = (PsiJavaFile) file;
            if (javaFile != null) {
                PsiElement element = javaFile.getClasses()[0];
                if (element != null) {
                    final PsiClass psiClass = (PsiClass) element;
                    if (psiClass != null && !psiClass.isInterface()) {
                        try {
                            PsiElementClassMember[] elementClassMembers = new PsiElementClassMember[psiClass.getAllFields().length];
                            for (int i = 0; i < psiClass.getAllFields().length; i++) {
                                elementClassMembers[i] = new PsiFieldMember(psiClass.getAllFields()[i]);
                            }
                            MyChooserBuilder builder = new MyChooserBuilder(project);
                            final MemberChooser dialog = builder.createBuilder(elementClassMembers);
                            SwingUtilities.invokeLater(new Runnable() {
                                public void run() {
                                    dialog.show();
                                    if (MemberChooser.CANCEL_EXIT_CODE == dialog.getExitCode()) {
                                        return;
                                    }
                                    executeGenerateActionLater(editor, javaFile, psiClass, dialog.getSelectedElements());
                                }
                            });
                        } catch (Exception e) {
                            Messages.showErrorDialog(e.getMessage(), "Exception");
                        }
                    }
                }
            }
        }
    }

    private void executeGenerateActionLater(final Editor editor, final PsiJavaFile javaFile, final PsiClass psiClass, final List<PsiElementClassMember> selectedElements) {
        Runnable writeCommand = new Runnable() {
            public void run() {
                ApplicationManager.getApplication().runWriteAction(new Runnable() {
                    public void run() {
                        try {
                            excuteGenerateAction(editor, javaFile, psiClass, selectedElements);
                        } catch (Exception e) {
                        }
                    }
                });
            }
        };

        CommandProcessor.getInstance().executeCommand(editor.getProject(), writeCommand, "GenerateEqualsAndHashCode", null);
    }

    private void excuteGenerateAction(Editor editor, PsiJavaFile javaFile, PsiClass psiClass, List<PsiElementClassMember> selectedElements) {
        List<PsiMember> psiMemberList = new ArrayList<PsiMember>();

        for (PsiElementClassMember classMember : selectedElements) {
            psiMemberList.add(classMember.getElement());
        }

        Project project = editor.getProject();
        PsiManager manager = PsiManager.getInstance(project);
        String code = generateCode(psiMemberList, psiClass);
        code = fixLineBreaks(code);
        Messages.showInfoMessage(code, "Code");
        Template template = new Template(code);
        template.read();


        PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
        insertMethod(manager, psiClass, elementFactory, template);
        insertImportStatement(elementFactory, template, javaFile);
    }

    private String generateCode(List<PsiMember> psiMemberList, PsiClass psiClass) {
        VelocityEngine velocityEngine = new VelocityEngine();
        try {
            velocityEngine.init();

            String templateResource = "/com/thoughtworks/yuyijq/generate/generateEqualsAndHashCode.vm";
            VelocityContext context = new VelocityContext();
            context.put("members", psiMemberList);
            context.put("class", psiClass.getName());
            StringWriter stringWriter = new StringWriter();

            velocityEngine.evaluate(context, stringWriter, "GenerateEqualsAndHashCode", TemplateLoader.load(templateResource));
            return stringWriter.getBuffer().toString();
        } catch (Exception e) {
            return null;
        }
    }

    private void insertMethod(PsiManager manager, PsiClass psiClass, PsiElementFactory elementFactory, Template template) {
        for (MethodElement methodElement : template.getMethodElements()) {
            String signature = methodElement.getSignatureElement().getSignatureStatement();
            String body = methodElement.getMethodBodyElement().getMethodBodyStatement();
            Messages.showInfoMessage(body, signature);
            PsiMethod method = elementFactory.createMethodFromText(signature + body, null);
            psiClass.addBefore(method, psiClass.getRBrace());
            manager.getCodeStyleManager().reformat(method);
        }
    }

    private void insertImportStatement(PsiElementFactory elementFactory, Template template, PsiJavaFile javaFile) {
        for (ImportElement importElement : template.getImportElements()) {
            PsiImportStatement statement = elementFactory.createImportStatementOnDemand(importElement.getImportStatement());
            PsiImportList importList = javaFile.getImportList();
            if (importList != null) {
                importList.add(statement);
            }
        }

        JavaCodeStyleManager.getInstance(javaFile.getProject()).optimizeImports(javaFile);
    }
}
